﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    /// <summary>
    /// http请求对象
    /// </summary>
    public class HttpRequest
    {
        private readonly HttpConnection _connection;
        /// <summary>
        /// http连接
        /// </summary>
        public HttpConnection Connection
        {
            get { return _connection; }
        }

        /// <summary>
        /// 临时数据文件文件存放目录
        /// </summary>
        private string _tempFileDirectory;

        private readonly NetLinkInfo _linkInfo;
        /// <summary>
        /// TcpClient
        /// </summary>
        internal NetLinkInfo LinkInfo
        {
            get { return _linkInfo; }
        }

        private readonly Stream _stream;
        /// <summary>
        /// http网络数据传输流
        /// </summary>
        public Stream Stream
        {
            get { return _stream; }
        }

        /// <summary>
        /// HTTP请求方式
        /// </summary>
        public string Method { get; private set; }

        /// <summary>
        /// 原始地址
        /// </summary>
        public string OriginUrl { get; private set; }

        /// <summary>
        /// 移除原始地址中携带QueryString参数后的地址
        /// </summary>
        public string Url { get; private set; }

        /// <summary>
        /// http协议版本
        /// </summary>
        public string HttpProtocolVer { get; private set; }



        /// <summary>
        /// 响应头部
        /// </summary>
        public IReadOnlyDictionary<string, string> Headers { get; private set; }

        /// <summary>
        /// 响应扩展头部-用于头中的重复项
        /// </summary>
        public IReadOnlyCollection<HeaderItem> ExtendHeaders { get; private set; }

        /// <summary>
        /// URL中携带的QueryString参数
        /// </summary>
        public IReadOnlyDictionary<string, string> QueryStringParams { get; private set; }

        private long _contentLength = 0;
        /// <summary>
        /// 内容大小
        /// </summary>
        public long ContentLength
        {
            get { return _contentLength; }
        }

        private BufferData _data = null;
        /// <summary>
        /// 请求缓存数据
        /// </summary>
        public BufferData Data
        {
            get { return _data; }
        }

        /// <summary>
        /// http会话
        /// </summary>
        public IHttpSession Session { get; set; }



        /// <summary>
        ///  构造函数
        /// </summary>
        /// <param name="connection">http连接</param>
        /// <param name="tempFileDirectory">临时数据存放目录</param>
        /// <param name="linkInfo">NetLinkInfo</param>
        /// <param name="stream">数据通信流</param>
        /// <param name="buffer">读取数据buffer</param>
        /// <param name="readLength">已读取数据长度</param>
        /// <exception cref="InvalidDataException">无效数据异常</exception>
        internal HttpRequest(HttpConnection connection, string tempFileDirectory, NetLinkInfo linkInfo, Stream stream, byte[] buffer, int readLength)
              : base()
        {
            this._connection = connection;
            this._tempFileDirectory = tempFileDirectory;
            this._linkInfo = linkInfo;
            this._stream = stream;

            int writeOffset = readLength;
            int findOffset = 0, index = -1;
            int totalLength = readLength, dataReadOffset = 0;
            do
            {
                index = CollectionEx.FindCollection<byte>(buffer, findOffset, readLength, WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY);
                if (index > 0)
                {
                    findOffset = index + WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY.Count;
                    dataReadOffset = index + WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY.Count;
                    break;
                }
                else
                {
                    findOffset = writeOffset - WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY.Count;
                }

                readLength = stream.Read(buffer, writeOffset, buffer.Length - writeOffset);
                writeOffset += readLength;
                totalLength += readLength;
            } while (true);


            //http头部固定为ASCII编码
            string requestHeaderStr = Encoding.ASCII.GetString(buffer, 0, index);
            //ZLoger.Debug(requestHeaderStr);
            if (string.IsNullOrWhiteSpace(requestHeaderStr))
            {
                throw new InvalidDataException();
            }

            string[] lines = requestHeaderStr.Split(WebConstnats.ENTER_NEWLINE_ARR, StringSplitOptions.RemoveEmptyEntries);

            //Request Headers
            List<HeaderItem> extendHeaders = null;
            var headers = new Dictionary<string, string>();
            string headerName, headerValue;
            for (int i = 1; i < lines.Length; i++)
            {
                string[] kv = lines[i].Split(WebConstnats.COLON_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
                if (kv.Length < 2)
                {
                    continue;
                }

                headerName = kv[0].Trim();
                headerValue = kv[1].Trim();

                if (headers.ContainsKey(headerName))
                {
                    if (extendHeaders == null)
                    {
                        extendHeaders = new List<HeaderItem>();
                    }
                    extendHeaders.Add(new HeaderItem(headerName, headerValue));
                }
                else
                {
                    headers.Add(headerName, headerValue);
                }
            }
            this.Headers = headers;

            if (extendHeaders != null)
            {
                this.ExtendHeaders = extendHeaders;
            }


            //body数据
            long contentLength;
            if (headers.TryGetValue(HttpRequestHeaders.ContentLength, out string contentLengthStr))
            {
                int bodyLength = totalLength - dataReadOffset;
                if (long.TryParse(contentLengthStr, out contentLength))
                {
                    this._contentLength = contentLength;
                }

                byte[] bufferData = buffer.Skip(dataReadOffset).Take(bodyLength).ToArray();
                this._data = new BufferData(bufferData);
            }
            else
            {
                //HttpStatusCodes.LengthRequired
                contentLength = totalLength - dataReadOffset;
                if (contentLength > 0)
                {
                    this._contentLength = contentLength;
                    byte[] bufferData = buffer.Skip(dataReadOffset).Take((int)contentLength).ToArray();
                    this._data = new BufferData(bufferData);
                }
            }


            //请求行
            //eg: GET /google-earth-dbroot-parser - 副本.js http:/1.1
            //string srcSreqLine = System.Web.HttpUtility.UrlDecode(lines[0]);
            string srcSreqLine = System.Net.WebUtility.UrlDecode(lines[0]);
            int methodIndex = srcSreqLine.IndexOf(' ');
            int verIndex = srcSreqLine.LastIndexOf(' ');
            if (methodIndex >= 0)
            {
                this.Method = srcSreqLine.Substring(0, methodIndex).Trim();
            }

            if (methodIndex != verIndex)
            {
                string srcUrl = srcSreqLine.Substring(methodIndex, verIndex - methodIndex);
                string originUrl = Uri.UnescapeDataString(srcUrl).Trim();
                string url;
                index = originUrl.IndexOf(WebConstnats.QUESTION_MARK);
                if (index >= 0)
                {
                    string queryStringParameters = originUrl.Substring(index + 1);
                    var paramsDic = this.ParseQueryStringParameters(queryStringParameters);
                    this.QueryStringParams = paramsDic;
                    url = originUrl.Substring(0, index);
                }
                else
                {
                    url = originUrl;
                }

                this.OriginUrl = originUrl;
                this.Url = url;

                this.HttpProtocolVer = srcSreqLine.Substring(verIndex).Trim();
            }
        }


        /// <summary>
        /// 解析querystring参数字符串
        /// </summary>
        /// <param name="queryStringParameters">参数字符串</param>
        /// <returns>参数字典集合</returns>
        private Dictionary<string, string> ParseQueryStringParameters(string queryStringParameters)
        {
            //projectID=8&version=-1&name=叶枫
            if (string.IsNullOrEmpty(queryStringParameters))
            {
                return null;
            }

            var paramsDic = new Dictionary<string, string>();
            string[] keyValueParaArr = queryStringParameters.Split(WebConstnats.AND_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
            string[] keyValue;
            foreach (var keyValuePara in keyValueParaArr)
            {
                keyValue = keyValuePara.Split(WebConstnats.EQUAL_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
                if (keyValue.Length > 1)
                {
                    paramsDic[keyValue[0]] = keyValue[1];
                }
                else
                {
                    paramsDic[keyValue[0]] = null;
                }
            }

            return paramsDic;
        }


        /*
        private string GetRequestBody(IEnumerable<string> rows)
        {
            var target = rows.Select((v, i) => new { Value = v, Index = i }).FirstOrDefault(e => e.Value.Trim() == string.Empty);
            if (target == null)
            {
                return null;
            }

            var range = Enumerable.Range(target.Index + 1, rows.Count() - target.Index - 1);
            return string.Join(Environment.NewLine, range.Select(e => rows.ElementAt(e)).ToArray());
        }


        private Dictionary<string, string> GetRequestHeaders(IEnumerable<string> rows)
        {
            if (rows == null || rows.Count() <= 0)
            {
                return null;
            }

            var target = rows.Select((v, i) => new { Value = v, Index = i }).FirstOrDefault(e => e.Value.Trim() == string.Empty);
            var length = target == null ? rows.Count() - 1 : target.Index;
            if (length <= 1)
            {
                return null;
            }

            var range = Enumerable.Range(1, length - 1);
            return range.Select(e => rows.ElementAt(e)).ToDictionary(e => e.Split(':')[0], e => e.Split(':')[1].Trim());
        }

        private string GetRequestHeader(Stream stream)
        {
            var length = 0;
            var data = string.Empty;
            byte[] buffer = new byte[NetHelper.MTU_MAX];
            do
            {
                length = stream.Read(buffer, 0, NetHelper.MTU_MAX);
                data += Encoding.ASCII.GetString(buffer, 0, length);
            } while (length > 0 && !data.Contains(_HEADER_END_STR));

            return data;
        }

        */


        private Encoding _clientSupportEncoding = null;
        /// <summary>
        /// 获取客户端支持的文本编码
        /// </summary>
        /// <param name="defaultEncoding"></param>
        /// <returns></returns>
        public Encoding GetClientTextEncoding(Encoding defaultEncoding = null)
        {
            if (this._clientSupportEncoding == null)
            {
                this._clientSupportEncoding = this.PrimitiveGetClientSupportEncoding(defaultEncoding);
            }

            return this._clientSupportEncoding;
        }

        private Encoding PrimitiveGetClientSupportEncoding(Encoding defaultEncoding = null)
        {
            //指定响应可接受的字符集编码 
            //public const string AcceptCharset = "Accept-Charset";

            ////Accept-Encoding，指定响应可接受的内容编码。使用的什么压缩算法
            ////public const string AcceptEncoding = "Accept-Encoding";
            Encoding encoding = null;

            if (this.Headers.TryGetValue(HttpRequestHeaders.AcceptCharset, out string acceptCharset))
            {
                //acceptCharset:utf-8,iso-8859-1;q=0.5
                if (string.IsNullOrWhiteSpace(acceptCharset) || acceptCharset.Contains(WebConstnats.ASTERISK_MARK))
                {
                    //任意皆可,使用utf-8
                    return Encoding.UTF8;
                }

                acceptCharset = acceptCharset.ToLower();
                if (acceptCharset.Contains(WebConstnats.UTF_8))
                {
                    //支持utf-8
                    return Encoding.UTF8;
                }

                string[] strs = acceptCharset.Split(WebConstnats.COMMA_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
                foreach (var str in strs)
                {
                    try
                    {
                        encoding = EncodingEx.GetEncoding(str);
                        if (encoding != null)
                        {
                            return encoding;
                        }
                    }
                    catch (Exception ex)
                    {
                        ZLoger.Warn(ex);
                    }
                }
            }

            if (this.Headers.TryGetValue(HttpRequestHeaders.ContentType, out string contentType))
            {
                if (!string.IsNullOrWhiteSpace(contentType))
                {
                    //Content-Type: application/json; charset=utf-8
                    string[] strArr = contentType.Split(WebConstnats.AND_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var str in strArr)
                    {
                        if (str.Contains(WebConstnats.CHARSET))
                        {
                            int index = str.IndexOf(WebConstnats.EQUAL_MARK);
                            if (index > 0)
                            {
                                try
                                {
                                    string encodingName = str.Substring(index + 1);
                                    encoding = EncodingEx.GetEncoding(encodingName);
                                }
                                catch (Exception ex)
                                {
                                    ZLoger.Warn(ex);
                                }

                                break;
                            }
                        }
                    }
                }
            }

            if (encoding == null)
            {
                if (defaultEncoding == null)
                {
                    //默认使用UTF8编码
                    encoding = Encoding.UTF8;
                }
                else
                {
                    encoding = defaultEncoding;
                }
            }

            return encoding;
        }





        /// <summary>
        /// 获取请求数据
        /// </summary>
        /// <returns>请求数据</returns>
        public string GetBody()
        {
            if (this._contentLength == 0)
            {
                return string.Empty;
            }

            Encoding encoding = this.GetClientTextEncoding();
            if (this._data.DataType == BufferDataType.Bytes)
            {
                if (this._data.Bytes == null || this._data.Bytes.Length < this._contentLength)
                {
                    this.ReceiveAllData();
                }

                return encoding.GetString(this._data.Bytes);
            }
            else
            {
                throw new HttpException(HttpStatusCodes.RequestEntityTooLarge);
            }
        }

        /// <summary>
        /// 创建并返回临时文件路径
        /// </summary>
        /// <returns>临时文件路径</returns>
        public string CreateTempFilePath()
        {
            string filePath = Path.Combine(this._tempFileDirectory, $"{Guid.NewGuid().ToString()}.dat");
            DirectoryInfoEx.CheckFilePathDirectory(filePath);
            return filePath;
        }

        /// <summary>
        /// 接收本次请求的全部数据
        /// </summary>
        public void ReceiveAllData()
        {
            BufferData data = this._data;
            long contentLength = this._contentLength;
            BufferData bufferData;
            if (contentLength < WebConstnats.REQ_CONTENT_MAX_SIZE)
            {
                //数据存放于内存中
                long position;
                byte[] buffer;
                if (data == null)
                {
                    buffer = new byte[contentLength];
                    position = 0;
                }
                else
                {
                    if (data.Bytes.Length >= contentLength)
                    {
                        //数据已经全部接收
                        return;
                    }

                    buffer = new byte[contentLength];
                    Array.Copy(data.Bytes, 0, buffer, 0, data.Bytes.Length);
                    position = data.Bytes.Length;
                }

                using (var ms = new MemoryStream(buffer))
                {
                    ms.Position = position;
                    this.PrimitiveReceiveAllData(ms, contentLength);
                }

                bufferData = new BufferData(buffer);
            }
            else
            {
                //数据存放于文件中
                string filePath = this.CreateTempFilePath();
                using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                {
                    this.PrimitiveReceiveAllData(fs, contentLength);
                    bufferData = new BufferData(filePath, 0, fs.Length);
                }
            }

            this._data = bufferData;
        }

        private void PrimitiveReceiveAllData(Stream stream, long contentLength)
        {
            long modLength = contentLength - stream.Position;
            int readCount = NetHelper.MTU_MAX;
            byte[] buffer = new byte[readCount];
            int readedLength;

            while (true)
            {
                if (modLength < buffer.Length)
                {
                    readCount = (int)modLength;
                }

                readedLength = this._stream.Read(buffer, 0, buffer.Length - readCount);
                stream.Write(buffer, 0, readedLength);
                modLength = contentLength - stream.Position;

                if (modLength <= 0)
                {
                    break;
                }
            }
        }






        /// <summary>
        /// 接收HttpFormData数据
        /// </summary>
        /// <returns>HttpFormData数据</returns>
        /// <exception cref="HttpException"></exception>
        public HttpFormData ReceiveHttpFormData()
        {
            /*********************************************************************************************
             * 多文件上传，在http请求头中的数据长度通常为0（当然也不一定）
             * 协议格式
             * 可能存在的项               示例内容
             * 
             * 边界                       ----WebKitFormBoundaryxhx7syLOcWnCSeXb\r\n
             * 文件名信息                 Content-Disposition: form-data; name="file"; filename="1.txt"\r\n
             * 文件类型                   Content-Type: text/plain\r\n
             * 空行表示文件信息描述完成   \r\n
             * 文件数据                   ...\r\n
             * 边界                       ----WebKitFormBoundaryxhx7syLOcWnCSeXb\r\n
             * 文件名信息                 Content-Disposition: form-data; name="file"; filename="2.txt"\r\n
             * 文件类型                   Content-Type: text/plain\r\n
             * 空行表示文件信息描述完成   \r\n
             * 文件数据                   ...\r\n
             * 边界                       ----WebKitFormBoundaryxhx7syLOcWnCSeXb\r\n
             * 文件名信息                 Content-Disposition: form-data; name="file"; filename="3.txt"\r\n
             * 文件类型                   Content-Type: text/plain\r\n
             * 空行表示文件信息描述完成   \r\n
             * 文件数据                   ...\r\n
             * 结束边界                   ----WebKitFormBoundaryxhx7syLOcWnCSeXb\r\n
             **********************************************************************************************/

            HttpRequest request = this;
            string boundary = this.GetBoundary(request);
            byte[] boundaryBytes = Encoding.ASCII.GetBytes(boundary);
            string boundaryEnd = $"{WebConstnats.ENTER_NEWLINE}{boundary}--";
            byte[] boundaryEndBytes = Encoding.ASCII.GetBytes(boundaryEnd);
            int dataLength;
            byte[] buffer = this.CreateReceiveDataBuffer(request, out dataLength);

            long contentLength = request.ContentLength;
            if (contentLength < 0 || contentLength < dataLength)
            {
                throw new HttpException(HttpStatusCodes.LengthRequired);
            }

            long totalLength = dataLength;
            Stream httpStream = request.Stream;
            int readOffset = 0, writeOffset = dataLength;
            var files = new List<HttpFile>();
            int readLength;
            int index, boundaryIndex, boundaryEndIndex;
            int receiveDataLength, minReceiveDataLength = buffer.Length / 4;
            Encoding encoding = request.GetClientTextEncoding();
            bool continueReceive = false;
            HttpFile httpFile = null;
            int durationZeroCount = 0;

            try
            {
                //读取上传文件列表
                do
                {
                    receiveDataLength = buffer.Length - writeOffset;
                    if (receiveDataLength < minReceiveDataLength)
                    {
                        dataLength = writeOffset - readOffset;
                        if (buffer.Length - dataLength < minReceiveDataLength)
                        {
                            //必须一次性解析的空间不够了，扩大buffer空间
                            byte[] buffer2 = new byte[buffer.Length * 2];
                            Array.Copy(buffer, readOffset, buffer2, 0, dataLength);
                            buffer = buffer2;
                        }
                        else
                        {
                            //当可接收数据大小小于最小值后，迁移数据
                            Array.Copy(buffer, readOffset, buffer, 0, dataLength);
                        }

                        writeOffset = dataLength;
                        readOffset = 0;
                    }

                    if (receiveDataLength > 0)
                    {
                        readLength = httpStream.Read(buffer, writeOffset, receiveDataLength);
                        if (readLength <= 0)
                        {
                            durationZeroCount++;
                            if (durationZeroCount >= WebConstnats.CONTINUITY_READ_DATA_LEN_ZERO_COUNT)
                            {
                                //当连续收到5次长为0 的数据,则断开连接.通常情况下是客户端断开了连接,但是服务端并不知道
                                break;
                            }
                            else
                            {
                                continue;
                            }
                        }
                        durationZeroCount = 0;
                        request.Connection.UpdateLastRequestTime();
                        writeOffset += readLength;
                        totalLength += readLength;
                    }


                    boundaryIndex = CollectionEx.FindCollection<byte>(buffer, readOffset, writeOffset - readOffset, boundaryBytes);
                    if (boundaryIndex >= 0)
                    {
                        do
                        {
                            //前一个文件上传完成，新文件开始
                            if (httpFile != null)
                            {
                                //当前文件上传完成，新文件开始
                                dataLength = boundaryIndex - readOffset - WebConstnats.ENTER_NEWLINE_BINARY.Count;
                                if (dataLength > 0)
                                {
                                    httpFile.Write(buffer, readOffset, dataLength);
                                }
                                httpFile.EndWrite();
                                files.Add(httpFile);
                                httpFile = null;
                                readOffset = boundaryIndex;
                            }

                            boundaryEndIndex = CollectionEx.FindCollection<byte>(buffer, boundaryIndex, writeOffset - boundaryIndex, WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY);
                            if (boundaryEndIndex > 0)
                            {
                                //http头部固定为ASCII编码
                                dataLength = boundaryEndIndex - boundaryIndex;
                                string uploadFileHeaderStr = encoding.GetString(buffer, boundaryIndex, dataLength);
                                string[] lineStrArr = uploadFileHeaderStr.Split(WebConstnats.ENTER_NEWLINE_ARR, StringSplitOptions.RemoveEmptyEntries);
                                httpFile = new HttpFile();

                                if (request.ContentLength > WebConstnats.REQ_CONTENT_MAX_SIZE)
                                {
                                    string filePath = request.CreateTempFilePath();
                                    httpFile.BeginWriteFile(filePath);
                                }
                                else
                                {
                                    httpFile.BeginWriteBuffer();
                                }

                                foreach (var lineStr in lineStrArr)
                                {
                                    if (lineStr.Contains(boundary))
                                    {
                                        continue;
                                    }

                                    index = lineStr.IndexOf(HttpRequestHeaders.ContentType);
                                    if (index >= 0)
                                    {
                                        //Content-Type: text/plain
                                        httpFile.ContentType = lineStr.Substring(index + 1);
                                    }

                                    index = lineStr.IndexOf(HttpRequestHeaders.ContentDisposition);
                                    if (index >= 0)
                                    {
                                        this.ParseHttpFileHeader(index, httpFile, lineStr);
                                    }
                                }

                                //找到文件内容开始
                                readOffset = boundaryEndIndex + WebConstnats.DOUBLE_ENTER_NEWLINE_BINARY.Count;

                                boundaryIndex = CollectionEx.FindCollection<byte>(buffer, readOffset, writeOffset - readOffset, boundaryBytes);
                                if (boundaryIndex < 0)
                                {
                                    break;
                                }
                                else
                                {
                                    index = CollectionEx.FindCollection<byte>(buffer, readOffset, writeOffset - readOffset, boundaryEndBytes);
                                    if (index + WebConstnats.ENTER_NEWLINE_BINARY.Count == boundaryIndex)
                                    {
                                        //如果是同一个位置，则结束循环
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                continueReceive = true;
                                break;
                            }
                        } while (true);

                        if (continueReceive)
                        {
                            continueReceive = false;
                            continue;
                        }
                    }

                    index = CollectionEx.FindCollection<byte>(buffer, readOffset, writeOffset - readOffset, boundaryEndBytes);
                    if (index >= 0)
                    {
                        //要上传的所有文件都接收完成,结束接收
                        if (httpFile != null)
                        {
                            dataLength = index - readOffset;
                            if (dataLength > 0)
                            {
                                httpFile.Write(buffer, readOffset, dataLength);
                            }
                            httpFile.EndWrite();
                            files.Add(httpFile);
                        }
                        break;
                    }

                    if (httpFile != null)
                    {
                        //即没有上传完，也没有新文件产生，则直接追加数据
                        dataLength = writeOffset - readOffset;
                        if (dataLength > 0)
                        {
                            httpFile.Write(buffer, readOffset, dataLength);
                        }
                        readOffset = writeOffset;
                    }
                } while (totalLength < contentLength);

                return new HttpFormData(files);
            }
            catch
            {
                //如果发生异常,需要把已收到的数据释放
                foreach (var file2 in files)
                {
                    file2.Dispose();
                }
                files.Clear();

                throw;
            }
        }

        private void ParseHttpFileHeader(int index, HttpFile file, string lineStr)
        {
            //Content-Disposition: form-data; name="file"; filename="1867.txt"
            string contentDisposition = lineStr.Substring(index + HttpRequestHeaders.ContentDisposition.Length + 1);
            string[] headerItemStrArr = contentDisposition.Split(WebConstnats.SEMICOLON_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
            string[] headerKeyValue;
            List<HeaderItem> headers = null;
            HeaderItem headerItem;

            foreach (string headerItemStr in headerItemStrArr)
            {
                headerKeyValue = headerItemStr.Split(WebConstnats.EQUAL_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
                if (headerKeyValue.Length == 2)
                {
                    /************************************************************************************************************
                     * 浏览器: 
                     * Content-Disposition: form-data; name="file"; filename="accessories-dictionary.png"
                     * System.Net.Http.HttpClient    
                     * Content-Disposition: form-data; name=file; filename="=?utf-8?B?54ix5LiK5L2g5oiR5b6I5b+r5LmQLndtYQ==?="; filename*=utf-8''%E7%88%B1%E4%B8%8A%E4%BD%A0%E6%88%91%E5%BE%88%E5%BF%AB%E4%B9%90.wma
                     ************************************************************************************************************/
                    if (string.Equals(headerKeyValue[0].Trim(), "filename", StringComparison.OrdinalIgnoreCase))
                    {
                        file.Filename = headerKeyValue[1].Trim().Replace("\"", string.Empty);
                        continue;
                    }

                    if (string.Equals(headerKeyValue[0].Trim(), "filename*", StringComparison.OrdinalIgnoreCase))
                    {
                        var fileNameIndex = headerKeyValue[1].IndexOf("''");
                        if (fileNameIndex >= 0)
                        {
                            file.Filename = System.Net.WebUtility.UrlDecode(headerKeyValue[1].Substring(fileNameIndex + 2).Trim());
                        }
                        else
                        {
                            file.Filename = System.Net.WebUtility.UrlDecode(headerKeyValue[1].Trim());
                        }

                        continue;
                    }

                    if (string.Equals(headerKeyValue[0].Trim(), "name", StringComparison.OrdinalIgnoreCase))
                    {
                        file.Name = headerKeyValue[1].Trim().Replace("\"", string.Empty);
                        continue;
                    }

                    headerItem = new HeaderItem(headerItemStr.Trim());
                }
                else
                {
                    headerItem = new HeaderItem(headerItemStr.Trim());
                }

                if (headers == null)
                {
                    headers = new List<HeaderItem>();
                }
                headers.Add(headerItem);
            }

            file.Headers = headers;
        }

        private byte[] CreateReceiveDataBuffer(HttpRequest request, out int dataLength)
        {
            dataLength = 0;
            byte[] buffer;
            BufferData bufferData = request.Data;
            if (bufferData != null)
            {
                if (bufferData.DataType != BufferDataType.Bytes)
                {
                    throw new ApplicationException("内部逻辑错误,接收内存缓存数据时，请求中的数据不为内存数据");
                }

                if (bufferData.Bytes.Length == request.ContentLength)
                {
                    buffer = bufferData.Bytes.ToArray();
                    dataLength = buffer.Length;
                }
                else if (bufferData.Bytes != null)
                {
                    dataLength = bufferData.Bytes.Length;
                    if (bufferData.Bytes.Length < NetHelper.MTU_MAX)
                    {
                        buffer = new byte[NetHelper.MTU_MAX];
                        Array.Copy(bufferData.Bytes, 0, buffer, 0, bufferData.Bytes.Length);
                    }
                    else
                    {
                        buffer = bufferData.Bytes.ToArray();
                    }
                }
                else
                {
                    buffer = new byte[NetHelper.MTU_MAX];
                }
            }
            else
            {
                buffer = new byte[NetHelper.MTU_MAX];
            }

            return buffer;
        }

        private string GetBoundary(HttpRequest request)
        {
            //Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxhx7syLOcWnCSeXb
            string contentType;
            if (!request.Headers.TryGetValue(HttpRequestHeaders.ContentType, out contentType))
            {
                throw new HttpRequestException($"请求头中缺失\"{HttpRequestHeaders.ContentType}\"");
            }
            const string BOUNDARY_KEY = "boundary";
            int boundaryIndex = contentType.IndexOf(BOUNDARY_KEY);
            if (boundaryIndex < 0)
            {
                throw new HttpRequestException($"请求头中缺失\"{HttpRequestHeaders.ContentType}\"对应值\"{contentType}\"中不包含\"{BOUNDARY_KEY}\"关键字");
            }
            string boundary = contentType.Substring(boundaryIndex + BOUNDARY_KEY.Length + 1);
            boundary = boundary.Trim('\"');  //注:适配 System.Net.Http.HttpClient,这货发来的请求中,居然会带上双引号
            boundary = $"--{boundary}";//边界前面还需要多加两个--
            return boundary;
        }












        /// <summary>
        /// 解析http请求范围字符串
        /// </summary>
        /// <param name="reqRangeStr">http请求范围,当有多个范围时请求范围用逗号分隔(eg:0-50,100-150)</param>
        /// <returns></returns>
        public static List<HttpRequestRange> ParseHttpRequestRanges(string reqRangeStr)
        {
            /************************************************************************************************************
             * 请求头 
             * Range HTTP请求报头指示该服务器应返回的文档的一部分。可以一次请求一个Range头部的多个部分，服务器可以在多部分文档中发回这些范围。
             * 如果服务器发回范围，它将使用206 Partial Content该响应。
             * 如果范围无效，则服务器返回416 Range Not Satisfiable错误。
             * 服务器也可以忽略Range标题并用200状态码返回整个文档。
             * 语法：
             * Range: <unit>=<range-start>-
             * Range: <unit>=<range-start>-<range-end>
             * Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
             
             * 响应头
             * Content-Range: 在HTTP协议中，响应首部 Content-Range 显示的是一个数据片段在整个文件中的位置。
             * 语法：
             * Content-Range: <unit> <range-start>-<range-end>/<size>
             * Content-Range: <unit> <range-start>-<range-end>/*
             * Content-Range: <unit>* /<size>
             ************************************************************************************************************/

            //reqRangeStr:  bytes=327680-
            int index = reqRangeStr.IndexOf('=');
            string unit = reqRangeStr.Substring(0, index);
            if (!string.Equals(unit, WebConstnats.REQUEST_RANGE_UNIT))
            {
                ZLoger.Warn($"发现不支持的范围请求单位:\"{unit}\"");
            }

            string rangeStr = reqRangeStr.Substring(index + 1);
            string[] strs = rangeStr.Split(WebConstnats.COMMA_MARK_ARR, StringSplitOptions.RemoveEmptyEntries);
            var list = new List<HttpRequestRange>();
            foreach (var str in strs)
            {
                list.Add(new HttpRequestRange(unit, str));
            }

            return list;
        }
    }
}
